#External libraries imports
from pyparsing import *
import theano.tensor as T
__all__ = ("Expression")

testGlobals =\
{
  "T": T,
  "a": T.iscalar("a"),
  "b": T.iscalar("b")
}

theanoOperators =\
{
  "!": "T.eq({par}, 0)",
  "==": "T.eq({par1}, {par2})",
  "!=": "T.neq({par1}, {par2})",
  "&&": "T.switch(T.neq({par1},0), T.switch(T.neq({par2},0), 1, 0), 0)",
  "||": "T.switch(T.eq({par1},0), T.switch(T.eq({par2},0), 0, 1), 1)"
}

LowercaseIdentifier = Regex("[a-z][a-zA-Z0-9]*")

UppercaseIdentifier = Regex("[A-Z][a-zA-Z0-9]*")

Expression = Forward()

Integer = Word(nums)

def chain(ex, op): return ex + ZeroOrMore(Or([(Literal(o) + ex) for o in op]))

def theanoChain(operators, x):
  theanoCode = x[0]
  theanoOperator = ""
  for b in x[1:]:
    if b in operators: theanoOperator  = theanoOperators[b]
    else: theanoCode = theanoOperator.format(par1=theanoCode, par2=b)
  return theanoCode

BaseExpression = (
    Integer ^ UppercaseIdentifier ^ LowercaseIdentifier
    ^ (Literal("(") + Expression + Literal(")")).setParseAction(lambda x: "".join(x))
  )

UnaryExpression = (
    ((Optional(Literal("+")) + BaseExpression)) ^
    ((Literal("-") + BaseExpression)) ^
    ((Suppress(Literal("!")) + BaseExpression)#todo: evitare di theanizzare se ho costanti
      .setParseAction(lambda u: theanoOperators["!"].format(par=u[0])))
  )

AdditiveExpression = chain(chain(UnaryExpression, ["*", "/", "%"]), ["+", "-"])

ShiftExpression = (
    AdditiveExpression +
    ZeroOrMore(
      (Suppress(Literal("<<")) + AdditiveExpression)
      .setParseAction(lambda x: ["*2**", x[0]]) ^
      (Suppress(Literal(">>")) + AdditiveExpression)
      .setParseAction(lambda x: ["/2**", x[0]])
    )
  )

RelationalExpression = chain(ShiftExpression, ["<", "<=", ">", ">="])
RelationalExpression.setParseAction(lambda x: "".join(x))

EqualityExpression = chain(RelationalExpression, ["==", "!="])
EqualityExpression.setParseAction(lambda x: theanoChain(["==","!="], x))

BitExclusiveOrExpression = chain(chain(EqualityExpression, ["&"]), ["^"])

BitOrExpression = chain(BitExclusiveOrExpression, ["|"])
BitOrExpression.setParseAction(lambda x: "".join(x))

AndExpression = chain(BitOrExpression, ["&&"])
AndExpression.setParseAction(lambda x: theanoChain(["&&"], x))

OrExpression = chain(AndExpression, ["||"])
OrExpression.setParseAction(lambda x: theanoChain(["||"], x))

Expression << (OrExpression)
